// Copyright (c) 2009 Hypirinha contributers
// This program is made available under the terms of the MIT License.
package org.hypirinha.html.output;

import org.hypirinha.html.Content;
import org.hypirinha.html.output.features.Namespace;
import org.hypirinha.html.attributes.AnyAttribute;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import static org.hypirinha.html.output.features.TypeFilter.filter;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.util.Stack;

public class DomAdapter {

    public interface Feature {}

    private Namespace namespace;

    public DomAdapter(Feature... features) {
        this (0,
            filter(features, Namespace.class, Namespace.unspecified)
        );
    }

    private DomAdapter(int workaroundAmbiguousConstructors, Namespace namespace) {
        workaroundAmbiguousConstructors++;
        this.namespace = namespace;
    }

    public Document asDocument(Content content) {
        DocumentBuilderVisitor visitor = new DocumentBuilderVisitor(namespace);
        content.visit(visitor);
        return visitor.getDocument();
    }

    private static class DocumentBuilderVisitor implements Visitor {
        private Document document;
        private Stack<Node> stack = new Stack<Node>();
        private Namespace namespace;

        public DocumentBuilderVisitor(Namespace namespace) {
            this.namespace = namespace;
            try {
                document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
                stack.push(document);
            } catch (ParserConfigurationException e) {
                throw new RuntimeException(e);
            }
        }

        public void startTag(String tagName, AnyAttribute... attributes) {
            Element element = document.createElementNS(namespace.getUri(), tagName);
            for (AnyAttribute attribute : attributes) {
                element.setAttributeNS(namespace.getUri(), attribute.getName(), attribute.getValue());
            }
            stack.peek().appendChild(element);
            stack.push(element);
        }

        public void text(String text) {
            stack.peek().appendChild(document.createTextNode(text));
        }

        public void endTag() {
            stack.pop();
        }

        public Document getDocument() {
            return document;
        }
    }
}
